package studio.baka.satoripixeldungeon.levels;

import studio.baka.satoripixeldungeon.Assets;
import studio.baka.satoripixeldungeon.Bones;
import studio.baka.satoripixeldungeon.Dungeon;
import studio.baka.satoripixeldungeon.actors.Actor;
import studio.baka.satoripixeldungeon.actors.Char;
import studio.baka.satoripixeldungeon.actors.mobs.King;
import studio.baka.satoripixeldungeon.actors.mobs.Mob;
import studio.baka.satoripixeldungeon.items.Heap;
import studio.baka.satoripixeldungeon.items.Item;
import studio.baka.satoripixeldungeon.items.keys.SkeletonKey;
import studio.baka.satoripixeldungeon.levels.painters.Painter;
import studio.baka.satoripixeldungeon.messages.Messages;
import studio.baka.satoripixeldungeon.scenes.GameScene;
import studio.baka.satoripixeldungeon.tiles.DungeonTileSheet;
import com.watabou.noosa.Group;
import com.watabou.noosa.tweeners.AlphaTweener;
import com.watabou.utils.Bundle;
import com.watabou.utils.PathFinder;
import com.watabou.utils.Random;

public class CityBossLevel extends Level {

    {
        color1 = 0x4b6636;
        color2 = 0xf2f2f2;
    }

    private static final int TOP = 2;
    private static final int HALL_WIDTH = 11;
    private static final int HALL_HEIGHT = 15;
    private static final int CHAMBER_HEIGHT = 4;

    private static final int WIDTH = 32;

    private static final int LEFT = (WIDTH - HALL_WIDTH) / 2;
    private static final int CENTER = LEFT + HALL_WIDTH / 2;

    private int arenaDoor;
    private boolean enteredArena = false;
    private boolean keyDropped = false;

    @Override
    public String tilesTex() {
        return Assets.TILES_CITY;
    }

    @Override
    public String waterTex() {
        return Assets.WATER_CITY;
    }

    private static final String DOOR = "door";
    private static final String ENTERED = "entered";
    private static final String DROPPED = "droppped";

    @Override
    public void storeInBundle(Bundle bundle) {
        super.storeInBundle(bundle);
        bundle.put(DOOR, arenaDoor);
        bundle.put(ENTERED, enteredArena);
        bundle.put(DROPPED, keyDropped);
    }

    @Override
    public void restoreFromBundle(Bundle bundle) {
        super.restoreFromBundle(bundle);
        arenaDoor = bundle.getInt(DOOR);
        enteredArena = bundle.getBoolean(ENTERED);
        keyDropped = bundle.getBoolean(DROPPED);
    }

    @Override
    protected boolean build() {

        setSize(32, 32);

        Painter.fill(this, LEFT, TOP, HALL_WIDTH, HALL_HEIGHT, Terrain.EMPTY);
        Painter.fill(this, CENTER, TOP, 1, HALL_HEIGHT, Terrain.EMPTY_SP);

        int y = TOP + 1;
        while (y < TOP + HALL_HEIGHT) {
            map[y * width() + CENTER - 2] = Terrain.STATUE_SP;
            map[y * width() + CENTER + 2] = Terrain.STATUE_SP;
            y += 2;
        }

        int left = pedestal(true);
        int right = pedestal(false);
        map[left] = map[right] = Terrain.PEDESTAL;
        for (int i = left + 1; i < right; i++) {
            map[i] = Terrain.EMPTY_SP;
        }

        exit = width() + CENTER;
        map[exit] = Terrain.LOCKED_EXIT;

        arenaDoor = (TOP + HALL_HEIGHT) * width() + CENTER;
        map[arenaDoor] = Terrain.DOOR;

        Painter.fill(this, LEFT, TOP + HALL_HEIGHT + 1, HALL_WIDTH, CHAMBER_HEIGHT, Terrain.EMPTY);
        Painter.fill(this, LEFT, TOP + HALL_HEIGHT + 1, HALL_WIDTH, 1, Terrain.BOOKSHELF);
        map[arenaDoor + width()] = Terrain.EMPTY;
        Painter.fill(this, LEFT, TOP + HALL_HEIGHT + 1, 1, CHAMBER_HEIGHT, Terrain.BOOKSHELF);
        Painter.fill(this, LEFT + HALL_WIDTH - 1, TOP + HALL_HEIGHT + 1, 1, CHAMBER_HEIGHT, Terrain.BOOKSHELF);

        entrance = (TOP + HALL_HEIGHT + 3 + Random.Int(CHAMBER_HEIGHT - 2)) * width() + LEFT + (/*1 +*/ Random.Int(HALL_WIDTH - 2));
        map[entrance] = Terrain.ENTRANCE;

        for (int i = 0; i < length() - width(); i++) {
            if (map[i] == Terrain.EMPTY && Random.Int(10) == 0) {
                map[i] = Terrain.EMPTY_DECO;
            } else if (map[i] == Terrain.WALL
                    && DungeonTileSheet.floorTile(map[i + width()])
                    && Random.Int(21 - Dungeon.depth) == 0) {
                map[i] = Terrain.WALL_DECO;
            }
        }

        return true;
    }

    public int pedestal(boolean left) {
        if (left) {
            return (TOP + HALL_HEIGHT / 2) * width() + CENTER - 2;
        } else {
            return (TOP + HALL_HEIGHT / 2) * width() + CENTER + 2;
        }
    }

    @Override
    protected void createMobs() {
    }

    public Actor respawner() {
        return null;
    }

    @Override
    protected void createItems() {
        Item item = Bones.get();
        if (item != null) {
            int pos;
            do {
                pos =
                        Random.IntRange(LEFT + 1, LEFT + HALL_WIDTH - 2) +
                                Random.IntRange(TOP + HALL_HEIGHT + 2, TOP + HALL_HEIGHT + CHAMBER_HEIGHT) * width();
            } while (pos == entrance);
            drop(item, pos).setHauntedIfCursed(1f).type = Heap.Type.REMAINS;
        }
    }

    @Override
    public int randomRespawnCell() {
        int cell;
        if (bossisalive()) {
            do {
                cell = Random.Int(0, (width() - 1) * (height() - 1));
            } while (!passable[cell] || Actor.findChar(cell) != null || !heroFOV[cell]);
            return cell;
        } else {
            do {
                cell = entrance + PathFinder.NEIGHBOURS8[Random.Int(8)];
            } while (!passable[cell] || Actor.findChar(cell) != null);
            return cell;
        }
    }

    @Override
    public void occupyCell(Char ch) {

        super.occupyCell(ch);

        if (!enteredArena && outsideEntraceRoom(ch.pos) && ch == Dungeon.hero) {

            enteredArena = true;
            seal();

            for (Mob m : mobs) {
                //bring the first ally with you
                if (m.alignment == Char.Alignment.ALLY && !m.properties().contains(Char.Property.IMMOVABLE)) {
                    m.pos = Dungeon.hero.pos + (Random.Int(2) == 0 ? +1 : -1);
                    m.sprite.place(m.pos);
                    break;
                }
            }

            King boss = new King();
            boss.state = boss.WANDERING;
            int count = 0;
            do {
                boss.pos = Random.Int(length());
            } while (
                    !passable[boss.pos] ||
                            !outsideEntraceRoom(boss.pos) ||
                            (heroFOV[boss.pos] && count++ < 20));
            GameScene.add(boss);

            if (heroFOV[boss.pos]) {
                boss.notice();
                boss.sprite.alpha(0);
                boss.sprite.parent.add(new AlphaTweener(boss.sprite, 1, 0.1f));
            }

            set(arenaDoor, Terrain.LOCKED_DOOR);
            GameScene.updateMap(arenaDoor);
            Dungeon.observe();
        }
    }

    @Override
    public Heap drop(Item item, int cell) {

        if (!keyDropped && item instanceof SkeletonKey) {

            keyDropped = true;
            unseal();

            set(arenaDoor, Terrain.DOOR);
            GameScene.updateMap(arenaDoor);
            Dungeon.observe();
        }

        return super.drop(item, cell);
    }

    private boolean outsideEntraceRoom(int cell) {
        return cell / width() < arenaDoor / width();
    }

    @Override
    public String tileName(int tile) {
        switch (tile) {
            case Terrain.WATER:
                return Messages.get(CityLevel.class, "water_name");
            case Terrain.HIGH_GRASS:
                return Messages.get(CityLevel.class, "high_grass_name");
            default:
                return super.tileName(tile);
        }
    }

    @Override
    public String tileDesc(int tile) {
        switch (tile) {
            case Terrain.ENTRANCE:
                return Messages.get(CityLevel.class, "entrance_desc");
            case Terrain.EXIT:
                return Messages.get(CityLevel.class, "exit_desc");
            case Terrain.WALL_DECO:
            case Terrain.EMPTY_DECO:
                return Messages.get(CityLevel.class, "deco_desc");
            case Terrain.EMPTY_SP:
                return Messages.get(CityLevel.class, "sp_desc");
            case Terrain.STATUE:
            case Terrain.STATUE_SP:
                return Messages.get(CityLevel.class, "statue_desc");
            case Terrain.BOOKSHELF:
                return Messages.get(CityLevel.class, "bookshelf_desc");
            default:
                return super.tileDesc(tile);
        }
    }

    @Override
    public Group addVisuals() {
        super.addVisuals();
        CityLevel.addCityVisuals(this, visuals);
        return visuals;
    }
}
